﻿using System;
using System.Buffers.Binary;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using JT808.Gateway.Abstractions;
using JT808.Gateway.Session;
using JT808.Protocol;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace JT808.Gateway.Services
{
    internal class JT808NotifyHostedService : IHostedService
    {
        private readonly IJT808NotifyConsumer jT808NotifyConsumer;
        private readonly JT808SessionManager JT808SessionManager;
        private readonly ILogger logger;

        public JT808NotifyHostedService(IJT808NotifyConsumer consumer, JT808SessionManager sessionManager, ILoggerFactory factory)
        {
            jT808NotifyConsumer = consumer;
            JT808SessionManager = sessionManager;
            logger = factory.CreateLogger<JT808NotifyHostedService>();
        }

        public int BcdToInt(byte value)
        {
            int high = value >> 4;
            int low = value & 0xF;
            int number = 10 * high + low;
            return number;
        }

        public Task StartAsync(CancellationToken cancellationToken)
        {
            jT808NotifyConsumer.OnMessage((item) =>
            {
                if (item.notifyKey == "verified")
                {
                    var who = Encoding.UTF8.GetString(item.param);
                    IJT808Session session;
                    if (JT808SessionManager.TerminalPhoneNoSessions.TryGetValue(who, out session))
                    {
                        session.Verified = true;
                        logger.LogInformation($"terminal with sim=[{who}] has been verified");
                    }
                }
                else if (item.notifyKey == "PositionReport")
                {
                    Span<byte> bytes = item.param;
                    int year = BcdToInt(bytes[0]);
                    int month = BcdToInt(bytes[1]);
                    int day = BcdToInt(bytes[2]);
                    int hour = BcdToInt(bytes[3]);
                    int minute = BcdToInt(bytes[4]);
                    int second = BcdToInt(bytes[5]);
                    DateTime time = new DateTime(year + JT808Constants.DateLimitYear, month, day, hour, minute, second);
                    uint i = BinaryPrimitives.ReadUInt32BigEndian(bytes.Slice(6));
                    uint j = BinaryPrimitives.ReadUInt32BigEndian(bytes.Slice(10));
                    Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
                    string who = new string(Encoding.GetEncoding("GBK").GetChars(bytes.Slice(14).ToArray()));
                    logger.LogDebug($"Position Info from [{who}] at time [{time.ToString()}]-lng:{i.ToString()}, lat:{j.ToString()}");
                }
            });
            jT808NotifyConsumer.Subscribe();
            return Task.CompletedTask;
        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            jT808NotifyConsumer.Unsubscribe();
            return Task.CompletedTask;
        }
    }
}
